/**
* This file is part of OntoCompoFramework
*
* Copyright (C) 2011 -  Christian Brel
*
* @author   Main    Christian Brel
**/
package ontocompo.ui.selection;

import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;

import ontocompo.ui.appmanagement.Application;
import ontocompo.ui.appmanagement.ApplicationManager;
import ontocompo.ui.global.Resources;
import ontocompo.ui.screenmanagement.Screen;

public class Selector extends Observable implements MouseListener, Observer {

	private SelectionType selectionType;
	private GuideType guideType;
	private Boolean guideSelectionEnabled;
	//private JInternalFrame currentJInternalFrame;
	protected Application currentApplication;
	
	//private JComponent over;
	protected Border border;
	protected Color bg;
	protected boolean opaque;
	private Color hoverColor;
	protected Color selectedColor;
	
	private Color keepColor;
	private String keepColorDescription;
	
	protected Boolean selected;
	
	protected ArrayList<SelectedComponent> selectedComponents;
	private SelectedComponent lastSelectedComponent;

	public Selector() {
		this.opaque = false;
		this.hoverColor = new Color(200, 255, 255);;
		this.selectedColor = new Color(255, 255, 200);
		
		this.keepColor = Color.GREEN;
		this.keepColorDescription = "green";
		
		this.selected = false;
		
		this.selectedComponents = new ArrayList<SelectedComponent>();
		this.lastSelectedComponent = null;
		
		this.guideSelectionEnabled = false;
	}
	
	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
		this.selected = true;
		
		JComponent over = (JComponent) arg0.getSource();
		if(this.guideSelectionEnabled) {
			Boolean inSelection = false;
			
			for(SelectedComponent sc: this.selectedComponents) {
				if(sc.getComponent().equals(over)) {
					inSelection = true;
				}
			}
			if(!inSelection) {
				addJComponentToSelection(over, this.currentApplication);
			}
			
			if(this.guideType == null) {
				this.guideType = GuideType.COMPLETE_HELP;
			}
			
			switch(this.guideType) {
				case COMPLETE_HELP:
					completeHelp(over, this.currentApplication);
					break;
				case TASK_HELP:
					taskHelp(over, this.currentApplication);
					break;
				case FUNCT_HELP:
					functHelp(over, this.currentApplication);
					break;
				case LAYOUT_HELP:
					layoutHelp(over, this.currentApplication);
					break;
				default: ;
			}
			
			if(inSelection){
				this.restoreJComponent(this.border, this.bg, this.opaque, over);
			}
		} else {
			if(this.selectionType == null) {
				this.selectionType = SelectionType.ADD_SELECTION;
			}
			
			switch(this.selectionType) {
				case REPLACE_SELECTION:
					this.clearSelection();
					
					addJComponentToSelection(over, this.currentApplication);
					
					break;
				case ADD_SELECTION:
					Boolean inSelection = false;
					
					for(SelectedComponent sc: this.selectedComponents) {
						if(sc.getComponent().equals(over)) {
							inSelection = true;
						}
					}
					if(!inSelection) {
						addJComponentToSelection(over, this.currentApplication);
					} else {
						this.restoreJComponent(this.border, this.bg, this.opaque, over);
					}
					break;
				case REMOVE_SELECTION:
					SelectedComponent toRemove = null;
					for(SelectedComponent sc : this.selectedComponents) {
						if(sc.getComponent().equals(over)) {
							toRemove = sc;
						}
					}
					if(toRemove != null) {
						this.restoreJComponent(toRemove.getBorder(), toRemove.getBg(), toRemove.getOpaque(), toRemove.getComponent());
						this.selectedComponents.remove(toRemove);
						
						if(this.lastSelectedComponent != null && toRemove.equals(this.lastSelectedComponent)) {
							this.lastSelectedComponent = null;
						}
					} else {
						this.restoreJComponent(this.border, this.bg, this.opaque, over);
					}
					
					break;
				case INTERSECTION_SELECTION:
					System.err.println("This functionality is not yet implemented.");
					break;
				default: ;
			}
		}
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		JComponent over = (JComponent) arg0.getSource();
		
		this.border = over.getBorder();
		this.bg = over.getBackground();
		this.opaque = over.isOpaque();
		
		over.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.yellow, Color.orange));
		over.setBackground(this.hoverColor);
		over.setOpaque(true);
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		if(! this.selected ) {
			JComponent over = (JComponent) arg0.getSource();
			
			this.restoreJComponent(this.border, this.bg, this.opaque, over);
			
		} else {
			this.selected = false;
		}
	}

	@Override
	public void mousePressed(MouseEvent arg0) {

	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		if(arg0.getClass().isAssignableFrom(SelectionTools.class)) {
			if(arg1.getClass().isAssignableFrom(SelectionType.class)) {
				SelectionType st = (SelectionType) arg1;
				this.selectionType = st;
			}  else if(arg1.getClass().isAssignableFrom(GuideType.class)) {
				GuideType gt = (GuideType) arg1;
				this.guideType = gt;
			}  else if(arg1.getClass().isAssignableFrom(Boolean.class)) {
				this.guideSelectionEnabled = (Boolean) arg1;
			} else if(arg1.getClass().isAssignableFrom(String.class)) {
					String command = (String) arg1;
					if(command == "clear") {
						clearSelection();
					}
					if(command == "all") {
						selectAll();
					}
					if(command == "parentExtension") {
						parentSelectionExtension();
					}
					if(command == "taskExtension") {
						taskSelectionExtension();
					}
					if(command == "functionalityExtension") {
						functionalitySelectionExtension();
					}
			} else if(arg1.getClass().isAssignableFrom(LayoutSelection.class)) {
					LayoutSelection ls = (LayoutSelection) arg1;
					layoutSelectionExtension(ls);
			} else if(arg1.getClass().isAssignableFrom(Screen.class)) {
				Screen selectedScreen = (Screen) arg1;
				extractSelectionToScreen(selectedScreen);
			}
		}
		
		if(arg0.getClass().isAssignableFrom(ApplicationManager.class)) {
			Application currentApp = (Application) arg1;
			/*JInternalFrame jInternalFrame = currentApp.getInternalFrame();
			this.currentJInternalFrame = jInternalFrame;*/
			this.currentApplication = currentApp;
//			System.out.println("CHANGEMENT D'APP => VERS " + currentApp);
		}
	}

	protected void restoreJComponent(Border b, Color bg, Boolean opaque, JComponent component) {
		component.setBorder(b);
		component.setBackground(bg);
		component.setOpaque(opaque);
	}
	
	private void clearSelection() {
		for(SelectedComponent sc: this.selectedComponents) {
			this.restoreJComponent(sc.getBorder(), sc.getBg(), sc.getOpaque(), sc.getComponent());
		}
		
		this.selectedComponents.clear();
		
		this.lastSelectedComponent = null;
	}
	
	private void selectAll() {
		if(this.currentApplication != null) {
			ArrayList<JComponent> availabledComponents = this.currentApplication.getAvailabledJComponents();
			
			addJComponentsToSelection(availabledComponents, this.currentApplication);
		}
	}
	
	private void completeHelp(JComponent over, Application app) {
		this.taskHelp(over, app);
		this.functHelp(over, app);
		this.layoutHelp(over, app);
	}
	
	private void parentSelectionExtension() {
		if(this.lastSelectedComponent != null) {
			Application app = this.getAppFromSelectedComponent(this.lastSelectedComponent);
			
			if(app != null) {
				JComponent parent = app.getParentOfComponent(this.lastSelectedComponent.getComponent());
				
				if(parent != null) {
					addJComponentToSelection(parent, app);
				}
			}
		}
	}
	
	private Application getAppFromSelectedComponent(SelectedComponent sc) {
		Application selectedApp = null;
		for(Application app: Resources.LOADEDAPPLICATIONS) {
			if(app.containsJComponent(sc.getComponent())) {
				selectedApp = app;
			}
		}
		
		return selectedApp;
	}
	
	private void taskSelectionExtension() {
		if(this.lastSelectedComponent != null) {
			Application app = this.getAppFromSelectedComponent(this.lastSelectedComponent);
			
			if(app != null) {
				ArrayList<JComponent> taskExtension = app.getParentTaskContentFromSelectedComponent(this.lastSelectedComponent.getComponent());
				
				addJComponentsToSelection(taskExtension, app);
			}
		}
	}
	
	private void taskHelp(JComponent over, Application app) {
			ArrayList<JComponent> taskExtension = app.getParentTaskContentFromSelectedComponent(over);
			
			for(JComponent jc: taskExtension) {
				Boolean inSelection = false;
				for(SelectedComponent sc: this.selectedComponents) {
					if(sc.getComponent().equals(jc)) {
						inSelection = true;
						break;
					}
				}
				
				if(!inSelection) {
					Boolean keepComponent = keptComponentAskingQuestion("Component in " + this.keepColorDescription + " color is linked with selected component through a task. Do you want to keep this component?", jc, app);
					if(keepComponent) {
						addJComponentToSelection(jc, app);
					}
				}
			}
	}
	
	private boolean keptComponentAskingQuestion(String question, JComponent jc, Application app) {
		Border border = jc.getBorder();
		Color bg = jc.getBackground();
		boolean opaque = jc.isOpaque();
		
		jc.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.BLUE, Color.MAGENTA));
		jc.setBackground(this.keepColor);
		jc.setOpaque(true);
		
		int keepComponent = JOptionPane.showConfirmDialog(null, question);
		
		jc.setBorder(border);
		jc.setBackground(bg);
		jc.setOpaque(opaque);
		
		switch(keepComponent) {
			case JOptionPane.YES_OPTION:
				return true;
			case JOptionPane.NO_OPTION:
			case JOptionPane.CANCEL_OPTION:
				return false;
		}
		return false;
	}
	
	private void functionalitySelectionExtension() {
		if(this.lastSelectedComponent != null) {
			Application app = this.getAppFromSelectedComponent(this.lastSelectedComponent);
			
			if(app != null) {
				ArrayList<JComponent> functionalityExtension = app.getFunctionalityContentFromSelectedComponent(this.lastSelectedComponent.getComponent());
				
				addJComponentsToSelection(functionalityExtension, app);
			}
		}
	}
	
	private void functHelp(JComponent over, Application app) {
		ArrayList<JComponent> functionalityExtension = app.getFunctionalityContentFromSelectedComponent(over);
		
		for(JComponent jc: functionalityExtension) {
			Boolean inSelection = false;
			for(SelectedComponent sc: this.selectedComponents) {
				if(sc.getComponent().equals(jc)) {
					inSelection = true;
					break;
				}
			}
			
			if(!inSelection) {
				Boolean keepComponent = keptComponentAskingQuestion("Component in " + this.keepColorDescription + " color is linked with selected component through a functionality. Do you want to keep this component?", jc, app);
				if(keepComponent) {
					addJComponentToSelection(jc, app);
				}
			}
		}
	}
	
	private void layoutSelectionExtension(LayoutSelection ls) {
		if(this.lastSelectedComponent != null) {
			Application app = this.getAppFromSelectedComponent(this.lastSelectedComponent);
			
			if(app != null) {
				
				for(String direction:ls.getDirections()) {
					ArrayList<JComponent> layoutExtension = new ArrayList<JComponent>();
					
					if(ls.getStartPoint() == "first") {
						layoutExtension = app.getLayoutContentFromSelectedComponent(this.selectedComponents.get(0).getComponent(), direction);
					} else if(ls.getStartPoint() == "last") {
						layoutExtension = app.getLayoutContentFromSelectedComponent(this.lastSelectedComponent.getComponent(), direction);
					} else if(ls.getStartPoint() == "all") {
						for(SelectedComponent sc: this.selectedComponents) {
							ArrayList<JComponent> toAdd = app.getLayoutContentFromSelectedComponent(sc.getComponent(), direction);
							if(toAdd != null) {
								layoutExtension.addAll(toAdd);
							}
						}
					}
				
					addJComponentsToSelection(layoutExtension, app);
				}
			}
		}
	}
	
	private void layoutHelp(JComponent over, Application app) {
		String[] directions = {"topleft", "top", "topright", "left", "right", "bottomleft", "bottom", "bottomright"};
		
		ArrayList<JComponent> layoutExtension = new ArrayList<JComponent>();
		
		for(String direction: directions) {
			ArrayList<JComponent> directionExtension = app.getLayoutContentFromSelectedComponent(over, direction);
			if(directionExtension != null) {
				for(JComponent jc: directionExtension) {
					Boolean inSelection = false;
					for(SelectedComponent sc: this.selectedComponents) {
						if(sc.getComponent().equals(jc)) {
							inSelection = true;
							break;
						}
					}
					
					if(!inSelection && !layoutExtension.contains(jc)) {
						layoutExtension.add(jc);
						
						Boolean keepComponent = keptComponentAskingQuestion("Component in " + this.keepColorDescription + " color has '" +  direction + "' position in relation to selected component. Do you want to keep this component?", jc, app);
						if(keepComponent) {
							addJComponentToSelection(jc, app);
						}
					}
				}
			}
		}
		
		JComponent parent = app.getParentOfComponent(over);
		
		if(parent != null) {
			Boolean inSelection = false;
			for(SelectedComponent sc: this.selectedComponents) {
				if(sc.getComponent().equals(parent)) {
					inSelection = true;
					break;
				}
			}
			
			if(!inSelection) {
				Boolean keepComponent = keptComponentAskingQuestion("Component in " + this.keepColorDescription + " color is parent component of selected component. Do you want to keep this component?", parent, app);
				if(keepComponent) {
					addJComponentToSelection(parent, app);
				}
			}
		}
		
	}
	
	private void addJComponentsToSelection(ArrayList<JComponent> components, Application app) {
		for(JComponent jc: components) {
			addJComponentToSelection(jc, app);
		}
	}
	
	protected void addJComponentToSelection(JComponent jc, Application app) {
//		System.out.println(app);
		
		Boolean inSelection = false;
		
		for(SelectedComponent sc: this.selectedComponents) {
			if(sc.getComponent().equals(jc)) {
				inSelection = true;
				break;
			}
		}
		
		if(!inSelection) {
			jc.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.red, Color.yellow));
			jc.setBackground(this.selectedColor);
			jc.setOpaque(true);
			
			SelectedComponent sc = new SelectedComponent(this.border, this.bg, this.opaque, jc, app);
			this.selectedComponents.add(sc);
			
			this.lastSelectedComponent = sc;
		}
	}
	
	private void extractSelectionToScreen(Screen selectedScreen) {
		if(! this.selectedComponents.isEmpty()) {
			 for(SelectedComponent sc: this.selectedComponents) {
				 if(!sc.isExtract()) {
					 SelectedComponent toExtract = null;
					 
					 SelectedComponent parentSC = getParentIfInSelection(sc);
					 
					 if(parentSC != null) {
						 toExtract = parentSC;
						 this.setSonsExtracted(parentSC.getComponent(), parentSC.getApplication());
					 } else {
						 toExtract = sc;
					 }
					 
					 this.restoreJComponent(toExtract.getBorder(), toExtract.getBg(), toExtract.getOpaque(), toExtract.getComponent());
					 JComponent jc = toExtract.getApplication().extractComponent(toExtract.getComponent(), this);
					 selectedScreen.addInBulk(jc);
					 toExtract.setExtracted();
				 }
			 }
			 
			 this.selectedComponents.clear();
			 this.lastSelectedComponent = null;
		}
	}
	
	private SelectedComponent getParentIfInSelection(SelectedComponent sc) {
		
		JComponent parent = sc.getApplication().getValidParentOfComponent(sc.getComponent());
		
		if(parent != null) {
			for(SelectedComponent selectedComponent: this.selectedComponents) {
				if(parent.equals(selectedComponent.getComponent())) {
					return selectedComponent;
				}
			}
		}
		
		return null;
	}
	
	private void setSonsExtracted(JComponent parentComponent, Application app) {
		ArrayList<JComponent> sons = app.getValidSonsOfComponent(parentComponent);
		
		for(JComponent son: sons) {
			for(SelectedComponent sc: this.selectedComponents) {
				if(sc.getComponent().equals(son)) {
					this.restoreJComponent(sc.getBorder(), sc.getBg(), sc.getOpaque(), sc.getComponent());
					sc.setExtracted();
					break;
				}
			}
			if(son.getComponents().length > 0) {
				setSonsExtracted(son, app);
			}
		}
	}

}
